#include "peer_handshake.h"
#include "listen.h"
#include <unistd.h>
#include "files_check.h"
#include "exceptions.h"
#include "download.h"
#include "general.h"
#include <algorithm>

namespace torrent {

Download::Downloads Download::m_downloads;

Download::Download(const bencode& b) :
    m_bytesUploaded(0),
	m_bytesDownloaded(0),
	m_tracker(NULL),
	m_checked(false),
	m_settings(DownloadSettings::global())
{
	try {

		m_me = Peer(generateId(), "", Listen::port());
		m_name = b["info"]["name"].asString();

		m_files.set(b["info"]);
		m_files.openAll();

		m_tracker = new TrackerQuery(this);

		m_tracker->set(b["announce"].asString(),
				calcHash(b["info"]),
				m_me);

		FilesCheck::check(&m_files, this, HASH_COMPLETED);

		m_delegator = Delegator(&m_files.bitfield(),
					m_files.chunkSize(),
					m_files.chunkSize(m_files.chunkCount() - 1));

	} catch (const bencode_error& e) {

		m_files.closeAll();
		delete m_tracker;

		throw local_error("Bad torrent file \"" + std::string(e.what()) + "\"");
	} catch (const local_error& e) {

		m_files.closeAll();
		delete m_tracker;

		throw e;
	}
}

Download::~Download() {
	// TODO Auto-generated destructor stub
}

void Download::start() {
	if (m_tracker->state() != TrackerQuery::STOPPED)
		return;

	m_tracker->state(TrackerQuery::STARTED, m_checked);

	insertService(Timer::current() + m_settings.chokeCycle * 2, CHOKE_CYCLE);
}

void Download::stop() {
	if (m_tracker->state() == TrackerQuery::STOPPED)
		return;

	m_tracker->state(TrackerQuery::STOPPED);

	removeService(CHOKE_CYCLE);

	// TODO, handle stopping of download correctly
}

Download* Download::getDownload(const std::string& hash) {
	Downloads::iterator itr;

	for (itr = m_downloads.begin(); itr != m_downloads.end(); ++itr) {
		 if ((*itr)->tracker().hash() == hash) {
			break;
		}
	}

	return itr != m_downloads.end() ? *itr : NULL;
}

void Download::addConnection(int fd, const Peer& p) {
	for (Connections::iterator it = m_connections.begin(); it != m_connections.end(); ++it) {
		if ((*it)->peer() == p) {
			::close(fd);
			return;
		}
	}

	if (countConnections() >= m_settings.maxPeers) {
		::close(fd);

		return;
	}

	PeerConnection* c = new PeerConnection();

	c->set(fd, p, this);
	m_connections.push_back(c);

	Peers::iterator itr = std::find(m_availablePeers.begin(), m_availablePeers.end(), p);

	if (itr != m_availablePeers.end())
		m_availablePeers.erase(itr);
}

int Download::countConnections() const {
	int s = m_connections.size();

	for (PeerHandshake::Handshakes::iterator it = PeerHandshake::handshakes().begin();
			it != PeerHandshake::handshakes().end(); ++it) {
		if ((*it)->download() == this) {
			s++;
		}
	}

	return s;
}


}
